package com.smasher.update.component.download.sys

import android.annotation.SuppressLint
import android.app.DownloadManager
import android.app.DownloadManager.Query
import android.app.DownloadManager.Request
import android.content.Context
import android.content.IntentFilter
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import androidx.core.content.ContextCompat
import com.smasher.core.thread.ThreadPool
import com.smasher.update.component.download.DownloadFinishListener

class DownloadExecutor(context: Context) : Handler.Callback {

    private val manager: DownloadManager =
        context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
    private val record: MutableMap<String, Long> = HashMap()
    private val receiver = DownloadReceiver()
    private var downloadFinishListener: DownloadFinishListener? = null


    private val handler: Handler

    init {
        val looper = Looper.getMainLooper()
        handler = Handler(looper, this)
    }

    /**
     * @param request request
     * @param tag     tag
     */
    fun download(request: Request, tag: String) {
        val id = manager.enqueue(request)
        record[tag] = id
        Log.d(TAG, "download: $id")
    }

    /**
     * @param request request
     */
    fun download(request: Request): Long {
        val id = manager.enqueue(request)
        handler.sendMessage(Message.obtain().apply {
            what = WHAT_DOWNLOAD_START
            obj = id
        })
        Log.d(TAG, "download: $id")
        return id
    }

    /**
     * @param tag tag
     * @return DownloadInfo
     */
    fun query(tag: String): DownloadInfo? {
        val id = record[tag]
        if (id != null) {
            return queryById(id)
        }
        return null
    }

    @SuppressLint("Range")
    fun queryById(id: Long): DownloadInfo? {

        val query = Query().apply {
            setFilterById(id) //筛选下载任务，传入任务ID，可变参数
        }

        try {
            manager.query(query).use { c ->
                if (c.moveToFirst()) {
                    val status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS))
                    val queryId = c.getLong(c.getColumnIndex(DownloadManager.COLUMN_ID))
                    val title = c.getString(c.getColumnIndex(DownloadManager.COLUMN_TITLE))
                    val description =
                        c.getString(c.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION))
                    val uri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_URI))
                    val localUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI))
                    val mediaType = c.getString(c.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE))
                    val reason = c.getString(c.getColumnIndex(DownloadManager.COLUMN_REASON))
                    val total = c.getLong(c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
                    val current =
                        c.getLong(c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
                    val lastModifiedTimestamp =
                        c.getLong(c.getColumnIndex(DownloadManager.COLUMN_LAST_MODIFIED_TIMESTAMP))

                    return DownloadInfo(
                        status,
                        title,
                        description,
                        uri,
                        localUri,
                        mediaType,
                        reason,
                        queryId,
                        total,
                        current,
                        lastModifiedTimestamp
                    )
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    fun queryFinishedById(id: Long): Boolean {
        val info = queryById(id)
        if (info != null) {
            val it = info.status
            val finished = when (it) {
                DownloadManager.STATUS_SUCCESSFUL -> true
                DownloadManager.STATUS_FAILED -> false
                else -> false
            }
            return finished
        }
        return false
    }

    fun getUri(id: Long): Uri? {
        return manager.getUriForDownloadedFile(id)
    }

    /**
     * @param url            url  demo as @see [https://download.qidian.com](https://download.qidian.com/apknew/source/1001745.apk)
     * @param title          title  demo as QQ.apk
     * @param description    description demo as 最新1.5.1版本下载中.....
     * @param targetFileName targetFileName demo as "1001745.apk"
     */
    fun buildDefaultRequest(
        url: String,
        title: String,
        description: String,
        mimeType: String,
        targetFileName: String
    ): Request {
        return DownloadRequestBuilder(url)
            .setTitle(title)
            .setDescription(description)
            .setMimeType(mimeType)
            .setNetworkType(Request.NETWORK_WIFI or Request.NETWORK_MOBILE)
            .allowScanningByMediaScanner()
            .setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
            .setDefaultDestination(targetFileName)
            .build()
    }

    fun registerReceiveForDownLoad(
        context: Context,
        listener: DownloadFinishListener
    ) {
        downloadFinishListener = listener
        Log.d(TAG, "registerReceiveForDownLoad: ")
        receiver.addFinishListener(listener)
        val intentFilter = IntentFilter().apply {
            addAction(DownloadManager.ACTION_NOTIFICATION_CLICKED)
            addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
        }

        ContextCompat.registerReceiver(
            context,
            receiver,
            intentFilter,
            ContextCompat.RECEIVER_EXPORTED
        )
    }

    fun unregisterReceiveForDownload(context: Context) {
        Log.d(TAG, "unregisterReceiveForDownload: ")
        context.unregisterReceiver(receiver)
    }

    override fun handleMessage(msg: Message): Boolean {
        when (msg.what) {
            WHAT_DOWNLOAD_START -> {
                //Log.i(TAG, "query:  WHAT_DOWNLOAD_START")
                val id = getDownloadId(msg.obj)
                Log.i(TAG, "query:  WHAT_DOWNLOAD_START $id")
                ThreadPool.getInstance(ThreadPool.PRIORITY_HIGH).submit {
                    if (id != null) {
                        val info = queryById(id)
                        Log.i(TAG, "query:  WHAT_DOWNLOAD_START $info")
                        when (info?.status) {
                            DownloadManager.STATUS_RUNNING -> queryRunning(id)
                            DownloadManager.STATUS_PAUSED -> queryRunning(id)
                            DownloadManager.STATUS_SUCCESSFUL -> queryRunning(id)
                            DownloadManager.STATUS_FAILED -> queryRunning(id)
                            else -> queryRunning(id)
                        }
                    }
                }
                return true
            }

            WHAT_DOWNLOAD_PROGRESS -> {
                //Log.i(TAG, "query:  WHAT_DOWNLOAD_PROGRESS")
                val id = getDownloadId(msg.obj)
                Log.i(TAG, "query:  WHAT_DOWNLOAD_PROGRESS $id")
                ThreadPool.getInstance(ThreadPool.PRIORITY_HIGH).submit {
                    if (id != null) {
                        val info = queryById(id)
                        Log.i(TAG, "query:  WHAT_DOWNLOAD_PROGRESS $info")
                        when (info?.status) {
                            DownloadManager.STATUS_RUNNING -> queryRunning(id)
                            DownloadManager.STATUS_PAUSED -> queryRunning(id)
                            DownloadManager.STATUS_SUCCESSFUL -> queryFinished(id)
                            DownloadManager.STATUS_FAILED -> queryFinished(id)
                            else -> queryRunning(id)
                        }
                    }
                }
                return true
            }

            WHAT_DOWNLOAD_FINISH -> {
                Log.i(TAG, "query:  WHAT_DOWNLOAD_FINISH")
                val id = getDownloadId(msg.obj)
                if (id != null) {
                    downloadFinishListener?.onDownload(id)
                }
                return true
            }

            else -> {
                return true
            }
        }
        return false
    }

    private fun queryRunning(id: Long) {
        handler.sendMessageDelayed(Message.obtain().apply {
            what = WHAT_DOWNLOAD_PROGRESS
            obj = id
        }, 1000)
    }

    private fun queryFinished(id: Long) {
        handler.sendMessageDelayed(Message.obtain().apply {
            what = WHAT_DOWNLOAD_FINISH
            obj = id
        }, 1000)
    }

    private fun getDownloadId(target: Any): Long? {
        if (target is Long) {
            val id = target as Long
            return id
        }
        return null
    }


    companion object {
        const val TAG = "Download"

        const val WHAT_DOWNLOAD_START = 0x01
        const val WHAT_DOWNLOAD_PROGRESS = 0x02
        const val WHAT_DOWNLOAD_FINISH = 0x03
    }

}
